﻿using AppSocketModel;  
using AppSocketService.Company; 
using BCGPSCommon; 
using BCGPSMongoData;
using LJD.IMS.Utility;
using MongoDB.Driver;
using System;
using System.Collections.Generic; 

namespace AppSocketService.Account
{
    /// <summary>
    /// 用户账户
    /// </summary>
    public class AccountService : ServiceBase, IAccountService
    {
        private MongoDBCommand<AccountDto> mongoDBCommand = new MongoDBCommand<AccountDto>("Account");
        private MongoDBCommand<VehicleDto> mongoDBCommandVehicle = new MongoDBCommand<VehicleDto>("AccountVehicle"); 
        private readonly IRedisService redisService; 

        #region 构造函数 
        public AccountService(IRedisService redisService)
        {
            this.redisService = redisService; 
        }
        public AccountService(IClaimsAccessor claimsAccesso, IRedisService redisService) : base(claimsAccesso)
        {
            this.redisService = redisService;
        }
        #endregion

        #region 获取全部账户 初始化REDIS用
        public List<AccountDto> InitRedisGetAll()
        { 
            return mongoDBCommand.SelectDocumentsAsync(s=>s.ParentAccountId ==null).GetAwaiter().GetResult();
        }
        #endregion

        #region 登录
        public AccountDto Login(AccountLoginDto accountLoginDto, string ip)
        {
            var dto = mongoDBCommand.SelectDocumentAsync(s => s.AccountCode == accountLoginDto.AccountCode).GetAwaiter().GetResult();
            if (dto == null)
                throw new ArgumentException("登录账户不存在");
            if (accountLoginDto.Password.SHA256(accountLoginDto.AccountCode) != dto.Password)
                throw new ArgumentException("账户密码不正确!");

            var update = GetUpdateDefinition(dto, ip);
            var accountDto = mongoDBCommand.UpdateDocumentAsync(s => s.AccountCode == accountLoginDto.AccountCode, update).GetAwaiter().GetResult();
            
            accountDto.SubAccountCount = GetSubAccountCount(accountDto.AccountId);
            accountDto.VehicleCount = GetVehicleCount(accountDto.Id);

            if (accountDto.SubAccountCount > 0)
            {
                int total = 0;
                GetSubAccountVehicleTotal(accountDto.Id, ref total);
                accountDto.VehicleTotal = total + accountDto.VehicleCount;
            }

            return accountDto;
        }

        #region  获取子账户车辆总数量
        private void GetSubAccountVehicleTotal(string accountId, ref int vehicleTotal)
        {
            var list = mongoDBCommand.SelectDocumentsAsync(s => s.ParentAccountId == accountId).GetAwaiter().GetResult();
            if (list == null || list.Count <= 0)
                return;
             
            foreach (var item in list)
            {
                vehicleTotal += GetVehicleCount(item.AccountId);
                GetSubAccountVehicleTotal(item.AccountId, ref vehicleTotal);
            }
        }
        #endregion

        #region 获取指定账户下的车辆总数
        private int GetVehicleCount(string accountId)
        {
            int a = mongoDBCommandVehicle.SelectDocumentsAsync(s => s.AccountId == accountId).GetAwaiter().GetResult().Count;
            return a;
        }
        #endregion

        #region   获取子账户数量
        private int GetSubAccountCount(string accountId)
        {
            return mongoDBCommand.SelectDocumentsAsync(s => s.ParentAccountId == accountId).GetAwaiter().GetResult()?.Count ?? 0;
        }
        #endregion

        private UpdateDefinition<AccountDto> GetUpdateDefinition(AccountDto dto, string ip)
        {

            return Builders<AccountDto>.Update
                .Set(s => s.LastLoginTime, DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc))
                .Set(s => s.LoginTimes, dto.LoginTimes + 1)
                .Set(s => s.LoginIp, ip);
        }
        #endregion

        #region 注册新账户
        public AccountDto RegisterNew(AccountRegisterDto registerDto)
        {
            var accountDto = registerDto.As<AccountDto>();
            var companyService = new CompanyService();
            var companyDto = companyService.GetCompany(registerDto.RegisterCode,registerDto.CompanyName) ?? throw new ArgumentException($"公司名称[{registerDto.CompanyName}]或注册码[{registerDto.RegisterCode}]不存在,请联系服务商!");

            accountDto.CompanyId = companyDto.CompanyId;

            if (!string.IsNullOrWhiteSpace(accountDto.AccountCode))
            {
                if (!StringHelper.IsEmail(accountDto.AccountCode))
                    throw new ArgumentException("注册的账户ID使用的邮箱格式不正确!");
            }
            else
                throw new ArgumentException("账户ID必填信息!");

            if (string.IsNullOrWhiteSpace(accountDto.Password))
                throw new ArgumentException("密码不能为空!");

            var dto = mongoDBCommand.SelectDocumentAsync(s => s.AccountCode == accountDto.AccountCode).GetAwaiter().GetResult();

            if (dto != null)
                throw new ArgumentException("该账户ID已经存在,请更换后再试!");

            accountDto.AccountRole = AccountRole.Company;
            accountDto.AccountCodeType = AccountCodeType.EMAIL;
            accountDto.AccountName = accountDto.AccountCode;
            accountDto.Password = accountDto.Password.SHA256(accountDto.AccountCode);
            accountDto.CreateTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc);
            
            mongoDBCommand.InsertDocumentAsync(accountDto).GetAwaiter().GetResult();

            var ret = mongoDBCommand.SelectDocumentAsync(s => s.CompanyId == accountDto.CompanyId && s.AccountCode == accountDto.AccountCode).GetAwaiter().GetResult();
            ret.Password = string.Empty;
            redisService.AddAsync(GeneralContainer.RedisNameSpaceAccount + ret.CompanyId, ret).GetAwaiter().GetResult();
            return ret;
        }
        #endregion

        #region 修改账户
        public AccountDto Update(AccountDto accountDto)
        {
            return UpdateAccount(accountDto);

        }

        private AccountDto UpdateAccount(AccountDto accountDto)
        {
            if (!string.IsNullOrWhiteSpace(accountDto.AccountCode))
            {
                if (!StringHelper.IsEmail(accountDto.AccountCode))
                    throw new ArgumentException("账户名称格式不正确!");
            }
            else
                throw new ArgumentException("账户名称是必填信息!");

            if (string.IsNullOrWhiteSpace(accountDto.Password))
                throw new ArgumentException("密码不能为空!");


            var dto = mongoDBCommand.SelectDocumentAsync(s => s.AccountCode == accountDto.AccountCode && s.Id != accountDto.AccountId).GetAwaiter().GetResult();
            if (dto != null)
                throw new ArgumentException("该用户名称已被占用,请更换后再试!");

            var update = Builders<AccountDto>.Update
                .Set(s => s.UpdateTime, DateTime.UtcNow)
                .Set(s => s.AccountName, accountDto.AccountName)
                .Set(s => s.Password, accountDto.Password.SHA256(accountDto.AccountCode));
            mongoDBCommand.UpdateDocumentAsync(s => s.Id == accountDto.AccountId, update).GetAwaiter().GetResult();

            var ret = mongoDBCommand.SelectDocumentAsync(s => s.CompanyId == accountDto.CompanyId && s.AccountCode == accountDto.AccountCode).GetAwaiter().GetResult();
            ret.Password = string.Empty; 
            return accountDto;
        }
        #endregion

        #region 修改账户密码 
        public void UpdatePassword(string accountId, string password)
        {
            if (password.Trim().Length < 6)
                throw new ArgumentException("密码不能小于6个字符!");

            var dto = mongoDBCommand.SelectDocumentAsync(s => s.Id == accountId).GetAwaiter().GetResult();
            if (dto == null)
                throw new ArgumentException("该用户不存在!");

            dto.Password = dto.Password.SHA256(dto.AccountCode);
            var update = Builders<AccountDto>.Update
                .Set(s => s.Password, dto.Password);
            mongoDBCommand.UpdateDocumentAsync(s => s.Id == accountId, update).GetAwaiter().GetResult(); 
        }
        #endregion

        #region 获取当前登录账户
        public AccountDto GetCurrentAccount()
        {
            return mongoDBCommand.SelectDocumentAsync(s => s.Id == claimsAccessor.AccountId).GetAwaiter().GetResult();
           
        }
        #endregion

        #region  获取指定账户
        public AccountDto GetAccount(string accountId)
        {
            var accountDto = mongoDBCommand.SelectDocumentAsync(s => s.Id == accountId).GetAwaiter().GetResult();
            if (accountDto == null)
                throw new ArgumentException("获取的账户不存在!");

            accountDto.Password = string.Empty;

            accountDto.SubAccountCount = GetSubAccountCount(accountDto.AccountId);
            accountDto.VehicleCount = GetVehicleCount(accountDto.Id);

            if (accountDto.SubAccountCount > 0)
            {
                int total = 0;
                GetSubAccountVehicleTotal(accountDto.Id, ref total);
                accountDto.VehicleTotal = total + accountDto.VehicleCount;
            }

            return accountDto;
        }
        #endregion

        #region 添加子账户
        public AccountDto AddNewSubAccount(AccountRequestDto accountRequestDto)
        {
            AccountDto currentAccount = GetCurrentAccount();
            if (currentAccount == null)
                throw new Exception("登录账号异常,请重新登录!!!");

            AccountDto accountDto = accountRequestDto.As<AccountDto>();

            if (string.IsNullOrWhiteSpace(accountDto.Password))
                throw new ArgumentException("密码不能为空!");

            var a = mongoDBCommand.SelectDocumentAsync(s => s.Id == accountDto.ParentAccountId).GetAwaiter().GetResult() ?? throw new ArgumentException("父账户不存在!");

            if (!string.IsNullOrWhiteSpace(accountDto.AccountCode))
            {
                if (!StringHelper.IsEmail(accountDto.AccountCode))
                    throw new ArgumentException("注册的账户使用的邮箱格式不正确!");
            }
            else
                throw new ArgumentException("账户必填信息!");

            var dto = mongoDBCommand.SelectDocumentAsync(s => s.AccountCode == accountDto.AccountCode).GetAwaiter().GetResult();
            if (dto != null)
                throw new ArgumentException("该账户已经被占用,请更换后再试!");

            accountDto.CompanyId = currentAccount.CompanyId;
            accountDto.AccountRole = AccountRole.Depart;
            accountDto.AccountCodeType = AccountCodeType.EMAIL;
            accountDto.Password = accountDto.Password.SHA256(accountDto.AccountCode);
            accountDto.CreateTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc);

            mongoDBCommand.InsertDocumentAsync(accountDto).GetAwaiter().GetResult();

            var ret = mongoDBCommand.SelectDocumentAsync(s => s.CompanyId == accountDto.CompanyId && s.AccountCode == accountDto.AccountCode).GetAwaiter().GetResult();
            ret.Password = string.Empty; 

            return accountDto;
        }
        #endregion

        #region 获取子账户列表
        public List<AccountDto> GetSubAccountList(string accountId)
        {
            if (string.IsNullOrEmpty(accountId))
                throw new ArgumentException("账号参数不能为空!");
            var list = mongoDBCommand.SelectDocumentsAsync(s => s.ParentAccountId == accountId).GetAwaiter().GetResult();

            if (list == null || list.Count <= 0)
                return list;
            else
            {
                list.ForEach(s => s.Password = string.Empty);
                foreach (var item in list)
                {
                    item.SubAccountCount = GetSubAccountCount(item.AccountId);
                    item.VehicleCount = GetVehicleCount(item.AccountId);
                    int total = 0;
                    GetSubAccountVehicleTotal(item.AccountId, ref total);
                    item.VehicleTotal = total + item.VehicleCount;

                }
                return list;
            }
        }
        #endregion

        #region 删除子账户
        public bool DeleteSubAccount(string accountId)
        {
            if (string.IsNullOrWhiteSpace(accountId))
                throw new ArgumentException("账户号不能为空!");
            var CurrentAccount = GetCurrentAccount();

            if (GetVehicleCount(accountId) > 0)
            {
                throw new ArgumentException("删除账户前,请先删除或转移账户下的车辆!");
            }
            if (GetSubAccountCount(accountId) > 0)
                throw new ArgumentException("删除账户前请先删除该账户下的子账户");

            var Ret = mongoDBCommand.DeleteDocumentAsync(s => s.Id == accountId).GetAwaiter().GetResult().DeletedCount;
            if (Ret > 0)
            { 
                return true;
            }
            else
                throw new ArgumentException("没有数据被删除!");
        }
        #endregion


        #region 获取全部子账户Id
        public List<string> GetSubAccountIdAll(string accountId)
        {
            List<string> list = new List<string>();
            list.Add(accountId);
            GetSub(accountId, ref list);
            return list;
        }
        private void GetSub(string accountId,ref List<string> retlist)
        {
            var list = mongoDBCommand.SelectDocumentsAsync(s => s.ParentAccountId == accountId).GetAwaiter().GetResult();
            if (list == null || list.Count <= 0)
                return;
            foreach (var item in list)
            {
                retlist.Add(item.AccountId);
                GetSub(item.AccountId, ref retlist);
            }
        }
        #endregion

        #region SYSTEM 获取全部账户
        public PageableList<AccountDto> SystemGetAll(int? companyId,string accountCode, AccountRole? accountRole, AccountCodeType? accountCodeType,bool desc,int index,int size)
        {
            index = index > 0 ? index - 1 : 0; 

            List<string> wheres = new List<string>();

            if (companyId.HasValue)
                wheres.Add($"CompanyId:{companyId.Value}");

            if(accountCodeType.HasValue)
                 wheres.Add($"AccountCodeType:{(int)accountCodeType}");

            if (!string.IsNullOrWhiteSpace(accountCode))
                wheres.Add($"AccountCode:/{accountCode}/");

            if (accountRole.HasValue) 
                wheres.Add($"AccountRole:{(int)accountRole.Value}"); 

            string str = "{" + string.Join(" , ", wheres) + "}";

            var list = mongoDBCommand.SelectDocumentsAsync(str,s=>s.CreateTime,desc,index*size,size).GetAwaiter().GetResult();
            foreach (var item in list.Items)
            {
                item.SubAccountCount = GetSubAccountCount(item.AccountId);
                item.VehicleCount = GetVehicleCount(item.Id);

                if (item.SubAccountCount > 0)
                {
                    int total = 0;
                    GetSubAccountVehicleTotal(item.Id, ref total);
                    item.VehicleTotal = total + item.VehicleCount;
                }

            }
            return list;
        }
        #endregion

        #region    获取公司账户总数
        public int GetCompanyAccountCount(int companyId)
        {
            var list = mongoDBCommand.SelectDocumentsAsync(s => s.CompanyId == companyId).GetAwaiter().GetResult();
            if (list != null)
                return list.Count;
            else
                return 0;
        }
        #endregion

        #region 移动账号
        public AccountDto MoveAccount(string accountId,string parentAccountId)
        {
            if (string.IsNullOrWhiteSpace(accountId) || string.IsNullOrWhiteSpace(parentAccountId))
                throw new ArgumentException("源账号和目标账号不能为空!");
              
            var accountDto = mongoDBCommand.SelectDocumentAsync(s => s.Id == accountId).GetAwaiter().GetResult() ?? throw new ArgumentException("源账号不存在!");

            var parentAccountDto = mongoDBCommand.SelectDocumentAsync(s => s.Id == parentAccountId).GetAwaiter().GetResult() ?? throw new ArgumentException("目的账号不存在!");

            //原地踏步
            if (accountDto.ParentAccountId == parentAccountId)
                return accountDto;

            //两个账号的公司一致 
                if (parentAccountDto.CompanyId != accountDto.CompanyId)
                    throw new ArgumentException("源或目标账号异常!");
 

            //上级不能移动到下级
            var list = GetSubAccountIdAll(accountId);
            if (list.Contains(parentAccountId))
                throw new ArgumentException("不能移动到自己的子集账号下!");

            //移动
            var update = Builders<AccountDto>.Update
                .Set(s => s.ParentAccountId, parentAccountId) 
                .Set(s => s.UpdateTime, DateTime.UtcNow);
            var dto = mongoDBCommand.UpdateDocumentAsync(s => s.Id == accountDto.AccountId, update).GetAwaiter().GetResult();
            return dto;
        }
        #endregion
    }
}